// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Author: wsfuyibing <682805@qq.com>
// Date: 2024-07-26

package logics

import (
	"fmt"
	"gitee.com/go-wares/framework-iris/framework/src/errs"
	"github.com/kataras/iris/v12"
	"reflect"
	"sync"
)

var Class Classes

type (
	Classes interface {
		Build(i iris.Context, logic Logic) (logical Logical, name string, err error)
	}

	classes struct {
		mapper map[string]string
		mu     *sync.RWMutex
	}
)

func (o *classes) Build(i iris.Context, logic Logic) (logical Logical, name string, err error) {
	var ok bool
	// Param
	// i for iris.Context.
	if i == nil {
		err = errs.ContextNotPassed
		return
	}
	// Param
	// logic for Logic
	if logic == nil {
		err = errs.LogicNotPassed
		return
	}
	// Creates
	// a logical instance.
	logical = logic()
	// Lock
	// for goroutine-safe.
	o.mu.Lock()
	defer func() {
		if v, vi := logical.(LogicalClassName); vi {
			v.SetClassName(name)
		}
		o.mu.Unlock()
	}()
	// Store by key.
	key := fmt.Sprintf(`%s:%s`, i.Method(), i.Request().URL.Path)
	// Recall
	// route url.
	if name, ok = o.mapper[key]; ok {
		return
	}
	// Parse
	// name with reflection.
	ref := reflect.TypeOf(logical)
	switch ref.Kind() {
	case reflect.Ptr:
		elem := ref.Elem()
		name = fmt.Sprintf(`%s.%s`, elem.PkgPath(), elem.Name())
	case reflect.Struct:
		name = fmt.Sprintf(`%s.%s`, ref.PkgPath(), ref.Name())
	default:
		name = ref.String()
	}
	// Update mapper.
	o.mapper[key] = name
	return
}

func (o *classes) init() *classes {
	o.mu = new(sync.RWMutex)
	o.mapper = make(map[string]string)
	return o
}
